setfenv(1, require'winapi')
require'winapi.winuser'

ffi.cdef[[
HMENU CreateMenu(void);
HMENU CreatePopupMenu(void);
BOOL DestroyMenu(HMENU hMenu);

HMENU GetMenu(HWND hWnd);

BOOL SetMenu(
     HWND hWnd,
     HMENU hMenu);

BOOL DrawMenuBar(HWND hWnd);

typedef struct tagMENUITEMINFOW
{
    UINT     cbSize;
    UINT     fMask;
    UINT     fType;
    UINT     fState;
    UINT     wID;
    HMENU    hSubMenu;
    HBITMAP  hbmpChecked;
    HBITMAP  hbmpUnchecked;
    ULONG_PTR dwItemData;
     LPWSTR   dwTypeData;
    UINT     cch;
    HBITMAP  hbmpItem;
}   MENUITEMINFOW,  *LPMENUITEMINFOW;

typedef MENUITEMINFOW const  *LPCMENUITEMINFOW;

BOOL InsertMenuItemW(
     HMENU hmenu,
     UINT item,
     BOOL fByPosition,
     LPCMENUITEMINFOW lpmi);

BOOL SetMenuItemInfoW(
     HMENU hmenu,
     UINT item,
     BOOL fByPositon,
     LPCMENUITEMINFOW lpmii);

BOOL RemoveMenu(
     HMENU hMenu,
     UINT uPosition,
     UINT uFlags
);

HMENU GetSubMenu(
     HMENU hMenu,
     int nPos
);

typedef struct tagTPMPARAMS
{
    UINT    cbSize;
    RECT    rcExclude;
}   TPMPARAMS;
typedef TPMPARAMS  *LPTPMPARAMS;

BOOL TrackPopupMenuEx(
     HMENU,
     UINT,
     int,
     int,
     HWND,
     LPTPMPARAMS);

]]

function CreateMenuBar()
	return own(checkh(ffi.C.CreateMenu()), DestroyMenu)
end

function CreateMenu()
	return own(checkh(ffi.C.CreatePopupMenu()), DestroyMenu)
end

function DestroyMenu(menu)
	checknz(ffi.C.DestroyMenu(menu))
	disown(menu)
end

GetMenu = ffi.C.GetMenu

function SetMenu(menu, window)
	local oldmenu = own(GetMenu(window), DestroyMenu)
	checknz(ffi.C.SetMenu(window, menu))
	disown(menu)
	return oldmenu
end

function DrawMenuBar(window)
	checknz(ffi.C.DrawMenuBar(window))
end

MF_INSERT            = 0x00000000
MF_CHANGE            = 0x00000080
MF_APPEND            = 0x00000100
MF_DELETE            = 0x00000200
MF_REMOVE            = 0x00001000
MF_BYCOMMAND         = 0x00000000
MF_BYPOSITION        = 0x00000400
MF_SEPARATOR         = 0x00000800
MF_ENABLED           = 0x00000000
MF_GRAYED            = 0x00000001
MF_DISABLED          = 0x00000002
MF_UNCHECKED         = 0x00000000
MF_CHECKED           = 0x00000008
MF_USECHECKBITMAPS   = 0x00000200
MF_STRING            = 0x00000000
MF_BITMAP            = 0x00000004
MF_OWNERDRAW         = 0x00000100
MF_POPUP             = 0x00000010
MF_MENUBARBREAK      = 0x00000020
MF_MENUBREAK         = 0x00000040
MF_UNHILITE          = 0x00000000
MF_HILITE            = 0x00000080
MF_DEFAULT           = 0x00001000
MF_SYSMENU           = 0x00002000
MF_HELP              = 0x00004000
MF_RIGHTJUSTIFY      = 0x00004000
MF_MOUSESELECT       = 0x00008000

MIIM_STATE       = 0x00000001
MIIM_ID          = 0x00000002
MIIM_SUBMENU     = 0x00000004
MIIM_CHECKMARKS  = 0x00000008
MIIM_TYPE        = 0x00000010
MIIM_DATA        = 0x00000020
MIIM_STRING      = 0x00000040
MIIM_BITMAP      = 0x00000080
MIIM_FTYPE       = 0x00000100

MFT_STRING           = MF_STRING
MFT_BITMAP           = MF_BITMAP
MFT_MENUBARBREAK     = MF_MENUBARBREAK
MFT_MENUBREAK        = MF_MENUBREAK
MFT_OWNERDRAW        = MF_OWNERDRAW
MFT_RADIOCHECK       = 0x00000200
MFT_SEPARATOR        = MF_SEPARATOR
MFT_RIGHTORDER       = 0x00002000
MFT_RIGHTJUSTIFY     = MF_RIGHTJUSTIFY

MFS_GRAYED           = 0x00000003
MFS_DISABLED         = MFS_GRAYED
MFS_CHECKED          = MF_CHECKED
MFS_HILITE           = MF_HILITE
MFS_ENABLED          = MF_ENABLED
MFS_UNCHECKED        = MF_UNCHECKED
MFS_UNHILITE         = MF_UNHILITE
MFS_DEFAULT          = MF_DEFAULT

HBMMENU_CALLBACK            = -1
HBMMENU_SYSTEM              = 1
HBMMENU_MBAR_RESTORE        = 2
HBMMENU_MBAR_MINIMIZE       = 3
HBMMENU_MBAR_CLOSE          = 5
HBMMENU_MBAR_CLOSE_D        = 6
HBMMENU_MBAR_MINIMIZE_D     = 7
HBMMENU_POPUP_CLOSE         = 8
HBMMENU_POPUP_RESTORE       = 9
HBMMENU_POPUP_MAXIMIZE      = 10
HBMMENU_POPUP_MINIMIZE      = 11

local function wcs_cch(s, cdata)
	if type(s) == 'string' then
		local ws, sz = MultiByteToWideChar(s)
		cdata.cch = sz
		return ws
	else
		cdata.cch = ffi.sizeof(s)-2 --TODO: this only works with WCHAR[] but not with WCHAR* !!!
		return s
	end
end

MENUITEMINFOW = struct{
	ctype = 'MENUITEMINFOW', size = 'cbSize', mask = 'fMask',
	fields = mfields{
		'id',				 	'wID', 					MIIM_ID, pass,
		'caption',			'dwTypeData',        MIIM_STRING, wcs_cch,
		'submenu',			'hSubMenu', 			MIIM_SUBMENU, pass,
		'bitmap',			'hbmpItem', 			MIIM_BITMAP, 'HBITMAP',
		'checked_bitmap',	'hbmpChecked', 		MIIM_CHECKMARKS, 'HBITMAP',
		'unchecked_bitmap','hbmpUnchecked', 	MIIM_CHECKMARKS, 'HBITMAP',
		'type',				'fType', 				MIIM_FTYPE, flags,
		'state',				'fState', 				MIIM_STATE, flags,
	},
}

GetSubMenu = ffi.C.GetSubMenu

function InsertMenuItem(menu, pos, info)
	if not info then pos, info = 0, pos end --pos is optional
	pos = pos-1
	own(GetSubMenu(menu, pos), DestroyMenu)
	checknz(ffi.C.InsertMenuItemW(menu, pos, true, MENUITEMINFOW(info)))
end

function SetMenuItem(menu, pos, info)
	pos = pos-1
	if info.submenu then
		own(GetSubMenu(menu, pos), DestroyMenu)
	end
	disown(info.submenu)
	checknz(ffi.C.SetMenuItemInfoW(menu, pos, true, MENUITEMINFOW(info)))
end

MF_BYCOMMAND  = 0x00000000
MF_BYPOSITION = 0x00000400

function RemoveMenuItem(menu, pos)
	pos = pos-1
	local sm = GetSubMenu(menu, pos)
	checknz(ffi.C.RemoveMenu(menu, pos, MF_BYPOSITION))
	return own(sm, DestroyMenu)
end

TPMPARAMS = struct{
	ctype = 'TPMPARAMS', size = 'cbSize',
	fields = {exclude_rect = {'rcExclude'}}
}

function TrackPopupMenu(menu, window, x, y, TPM, exclude_rect)
	local tpmp = TPMPARAMS{exclude_rect = exclude_rect}
	checknz(ffi.C.TrackPopupMenuEx(menu, flags(TPM), x, y, window, tpmp))
end

--showcase

if not ... then
require'winapi.showcase'
local window = ShowcaseWindow()
local mainm = CreateMenuBar()
local filem = CreateMenu()
local openi = InsertMenuItem(filem, {caption = '&Open', type = MFT_RADIOCHECK, state = MFS_CHECKED})
local exiti = InsertMenuItem(filem, {caption = 'E&xit', bitmap = HBMMENU_MBAR_CLOSE, state = MFS_CHECKED})
local filei = InsertMenuItem(mainm, {caption = 'File', submenu = filem})
local xxxxx = InsertMenuItem(mainm, {caption = 'Deleteme'})
local helpi = InsertMenuItem(mainm, {caption = 'Help', submenu = filem})
SetMenu(mainm, window)
RemoveMenuItem(mainm, 2)
DrawMenuBar(window)
TrackPopupMenu(filem, window, 300, 300)
MessageLoop()
end
